using System;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics.Contracts;

namespace Granite.Data
{
	/// <summary>
	/// This represents the information needed to make a RPC-style call such as to a stored procedure.
	/// </summary>
	public class ProcedureCall
	{
		readonly ProcedureCallParameters m_Parameters = new ProcedureCallParameters();

		/// <summary>
		/// This represents the information needed to make a RPC-style call such as to a stored procedure.
		/// </summary>
		/// <param name="procedureName"></param>
		public ProcedureCall(string procedureName)
		{
			if (procedureName == null)
				throw new ArgumentNullException("procedureName", "procedureName is null or empty.");
			if (String.IsNullOrEmpty(procedureName))
				throw new ArgumentException("procedureName is empty.", "procedureName");
			Contract.EndContractBlock();

			ProcedureName = procedureName;
		}

		/// <summary>
		/// Collection of parameters
		/// </summary>
		public ProcedureCallParameters Parameters
		{
			get { return m_Parameters; }
		}

		/// <summary>
		/// Name of procedure to be called
		/// </summary>
		public string ProcedureName { get; set; }

		/// <summary>
		/// Timeout for making the procedure call
		/// </summary>
		public TimeSpan? Timeout { get; set; }


		/// <summary>
		/// Add a new paramter value. Will throw and exception if it already exists.
		/// </summary>
		public void Add(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name", "name is null or empty.");
			if (String.IsNullOrEmpty(name))
				throw new ArgumentException("name is empty.", "name");
			Contract.EndContractBlock(); 
			
			m_Parameters.Add(name, null);
		}

		/// <summary>
		/// Add a new paramter value. Will throw and exception if it already exists.
		/// </summary>
		/// <param name="name">Parameter name, which will be normalized. This is not case senstive.</param>
		/// <param name="value">Value, which will be normalized.</param>
		public void Add(string name, object value)
		{
			if (name == null)
				throw new ArgumentNullException("name", "name is null or empty.");
			if (String.IsNullOrEmpty(name))
				throw new ArgumentException("name is empty.", "name");
			Contract.EndContractBlock();

			m_Parameters.Add(name, value);
		}

		/// <summary>
		/// Returns a <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.
		/// </returns>
		public override string ToString()
		{
			var result = new System.Text.StringBuilder();
			result.Append(ProcedureName);

			var @params = new List<String>();

			foreach (var key in m_Parameters.Keys)
			{
				var value = m_Parameters[key];

				if (value == null)
					@params.Add(key + " = NULL");
				else if (value is string)
					@params.Add(key + " = '" + (string)value + "'");
				else if (value is DateTime)
					@params.Add(key + " = '" + ((DateTime)value).ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture) + "'");
				else if (value is TimeSpan)
					@params.Add(key + " = '" + value + "'");
				else
					@params.Add(key + " = " + value);

			}

			result.Append(" " + String.Join(", ", @params.ToArray()));

			if (Timeout.HasValue)
				result.Append(" [Timout:" + Timeout.ToString() + "]");

			return result.ToString().Trim();

		}

	}
}
